package com.example.res.config;


import com.example.res.annotation.DistributionLock;
import com.example.res.annotation.DistributionLockParam;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 对springboot中aop切面编程的测试
 */
//切面类
@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RedisAopAspect {
    @Autowired
    private RedissonClient redissonClient;

    public RedisAopAspect() {
        log.info("分布锁 aop init");
    }

    /***
     * 定义切入点
     */
    @Pointcut("execution(@com.example.res.annotation.DistributionLock * *(..))")
    public void pointCut() {

    }


    @Around(value = "pointCut()")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        /////////////////////AOP 能取得的信息 start////////////////////////
//        log.info("目标方法名为:{}",pjp.getSignature().getName());
//        log.info("目标方法所属类的简单类名:{}" , pjp.getSignature().getDeclaringType().getSimpleName());
//        log.info("目标方法所属类的类名:{}", pjp.getSignature().getDeclaringTypeName());
//        log.info("目标方法声明类型:{}" , Modifier.toString(pjp.getSignature().getModifiers()));
//        log.info("目标方法返回值类型:{}" , method.getReturnType());
//        //获取传入目标方法的参数
//        Object[] args = pjp.getArgs();
//        for (int i = 0; i < args.length; i++) {
//            log.info("第{}个参数为:{}" ,(i + 1) , args[i]);
//        }
//        log.info("被代理的对象:{}" , pjp.getTarget());
//        log.info("代理对象自己:{}" , pjp.getThis());
        /////////////////////AOP 能取得的信息 end////////////////////////

        //取得注解对象数据
        DistributionLock lock = method.getAnnotation(DistributionLock.class);
        //分布式锁实际的key
        String lockKey = getRealDistributionLockKey(pjp, lock);
        RLock lock1 = redissonClient.getLock(lockKey);
        lock1.lock();
        try {
            return pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } finally {
            lock1.unlock();
        }
    }

    /**
     * 加了DistributionLockParam注解参数值，按照顺序返回list
     *
     * @param pjp
     * @return
     */
    public List<Object> getDistributionLockParamList(ProceedingJoinPoint pjp) {
        ArrayList<Object> distributionLockParamList = null;
        MethodSignature signature = ((MethodSignature) pjp.getSignature());
        //得到拦截的方法
        Method method = signature.getMethod();
        //获取方法参数注解，返回二维数组是因为某些参数可能存在多个注解
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
//        log.info("parameterAnnotations:{}",parameterAnnotations);
        //获取全部参数
        Object[] objects = pjp.getArgs();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation a : parameterAnnotations[i]) {
                if (a.annotationType() == DistributionLockParam.class) {
                    //初始化distributionLockParamList
                    if (distributionLockParamList == null) {
                        distributionLockParamList = new ArrayList();
                    }
                    //获得参数值
                    Object o = objects[i];
                    distributionLockParamList.add(o);
                }
            }
        }

        return distributionLockParamList;
    }

    /**
     * 加了DistributionLockParam注解参数值，拼接成字符串
     *
     * @param pjp
     * @param lock
     * @return
     */
    public String getDistributionLockParamStr(ProceedingJoinPoint pjp, DistributionLock lock) {
        List<Object> distributionLockParamList = getDistributionLockParamList(pjp);
        if (distributionLockParamList != null && distributionLockParamList.size() > 0) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < distributionLockParamList.size(); i++) {
                Object param = distributionLockParamList.get(i);
                sb.append(lock.delimiter());
                sb.append(param);
            }
            return sb.toString();
        }
        return "";
    }

    /**
     * 返回分布式锁key完整的key
     *
     * @param pjp
     * @param lock
     * @return
     */
    public String getRealDistributionLockKey(ProceedingJoinPoint pjp, DistributionLock lock) {
        String distributionLockParamStr = getDistributionLockParamStr(pjp, lock);
        return lock.key().concat(distributionLockParamStr);
    }

}
